package com.itheima.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author huyoufu <https://github.com/huyoufu>
 * @TIME 2020-08-07 10:22
 * @description
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        //System.out.println("\n");
        //System.out.println("我是上头派来的巡查员 来康康你们!!");
        //System.out.println("正在被巡视的对象:"+bean);

        //可以判断的!!!!
        //bean.getClass() 获取被巡查对象的类
        //bean.getClass().isAnnotationPresent(Service.class) 判断被巡查对象的类是否包含有service注解
        if (bean.getClass().isAnnotationPresent(Service.class)){
            //System.out.println("我逮着你了!!!");
            //service不是我心中所想!!!

            //使用动态代理技术 修改这个对象
            /**
             * newProxyInstance，方法有三个参数：
             * loader: 用哪个类加载器去加载代理对象
             * interfaces:动态代理类需要实现的接口
             * h:动态代理方法在执行时，会调用h里面的invoke方法去执行
             *
             * 第一个参数 随便自己写类获取到类加载器就可以用
             * 第二个参数 改变某个对象 还必须跟原来对象实现同样的接口
             *  bean.getClass().getInterfaces 获取原本对象它所有实现所有接口们
             * 第三个参数 就是您使用动态代理技术创建出来这个对象 真正执行的方法都是由它来执行 invocationHandler
             *
             */
            Object newProxyInstance = Proxy.newProxyInstance(
                    MyBeanPostProcessor.class.getClassLoader(),
                    bean.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                            //System.out.println("动态代理对象正在执行"+method.getName()+"方法");

                            //调用原本的方法!!!!

                            //声明一下原本方法的返回值
                            Object result=null;

                            System.out.println("原本对象就要执行方法了***************\n");

                            //传参
                            //第一个参数 原本对象
                            //第二个参数 运行的时候需要参数
                            result=method.invoke(bean,args);

                            System.out.println("原本对象的方法执行完了***************\n");

                            return result;
                        }
                    }

            );
            //返回的意思 告诉框架 原来扔了 用我这个!!!
            return newProxyInstance;
        }
        return null;
    }
}
